package loaderlib.loader
{
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.utils.Dictionary;
	
	import loaderlib.agent.IRequester;

	/**
	 * the manager of loader.
	 * 
	 * this class manager the loader queue and communicate with requester.
	 * 
	 * @author TangZhen
	 * 
	 */
	public class LoaderManager extends EventDispatcher
	{
		
		/**
		 * @private
		 * a dictionary which map(url->RequestHeader)
		 * 
		 * <span><p>
		 * <code>requesthash</code> handle the url to RequestHeader relationship.
		 * </p></span>
		 * 
		 * @default a empty dictionary. 
		 */
		private var requestHash:Dictionary=new Dictionary;
				
		
		/**
		 * the initialization function
		 * 
		 * <span><p>
		 * It's a empty function.
		 * </p></span>
		 * 
		 * @param target
		 * 
		 */
		public function LoaderManager( )
		{
			//TODO: implement function
			super( null );
		}
		
		/**
		 * insertRequest: insert the request in associated RequestHeader.
		 * 
		 * <span><p>
		 * first it check wheather exist the associated header. if exist, append the request on it. else add a
		 * new header in request hash.
		 * </p></span>
		 * 
		 * @param request
		 * @param hashTable
		 * 
		 */
		internal function insertRequest(request:Request, hashTable:Dictionary):void
		{
			var header:RequestHeader = hashTable[request.url];
			if (header)
			{
				header.appendRequest(request)
			}
			else
			{
				header = RequestHeader.initHeader(request);
				if(header)
				{
					this.requestHash[request.url] = header
				}
			
			}
		}
		
		/**
		 * insertRequester: insert a Requester in associated header queue.
		 * 
		 * <span><p>
		 * It's public function, that server the outer requester usage.
		 * </p></span>
		 * @param requester
		 * 
		 */
		public function insertRequester( requester:IRequester ):void
		{
			var request:Request = new Request(requester)
			this.insertRequest(request, this.requestHash)
			this.insertRequest(request, TagHeader.tagHash)	 
		}
		
		/**
		 * deleteRequest delete the request on the associated queue.
		 * 
		 * <span><p>
		 * first it delete request on queue associated the request.url. for most usage, it's the right place.
		 * if the request isn't on such queue, it delete request on all queues.
		 * </p></span> 
		 * 
		 * @param request the deleted request.
		 * @param hashTable  the hash table, function being deleted
		 * 
		 */
		internal function deleteRequest(request:Request, hashTable:Dictionary):void
		{
			var header:RequestHeader = hashTable[request.url]
			if(header)
			{
				if(!header.deleteRequest(request))
				{
					for each( header in hashTable)
					{
						header.deleteRequest(request)
					}
				}
			}
		}
		
		/**
		 * delete requester on associated queue.
		 * 
		 * <span><p>
		 * It's public function, that server the outer requester usage.
		 * </p></span> 
		 * 
		 * @param requester
		 * 
		 */
		public function deleteRequester(requester:IRequester):void
		{
			var request:Request = new Request(requester)
			this.deleteRequest(request,this.requestHash);
		}
		
		/**
		 * update requester
		 * 
		 * <span><p>
		 * It's public function, that server the outer requester usage.
		 * </p></span>
		 *  
		 * @param requester
		 * @param hashTable
		 */
		public function updateRequester(requester:IRequester):void
		{
			var request:Request = new Request(requester)
			this.updateRequest(request,this.requestHash)
		}
		
		/**
		 * update request on associated queue
		 * 
		 * <span><p>
		 * If find invalde issue, it delete exist request on the wrong queue and insert on the right queue.
		 * </p></span> 
		 * 
		 * @param request
		 * @param hashTable
		 */		
		public function updateRequest(request:Request, hashTable:Dictionary):void
		{
			var header:RequestHeader = hashTable[request.url]
			if(header && header.hasRequest(request))
			{
				return
			}
			else
			{
				this.deleteRequest(request, hashTable)
				this.insertRequest(request, hashTable)
			}
		}
		
		public function get info():XML
		{
			var info:XML= <info></info>
			for each(var header:Header in this.requestHash)
			{
				info.appendChild(header.info)
			}
			return info
		}
		
		public function get bytesLoaded():Number
		{
			var bytesNum:Number = 0
			for each(var header:RequestHeader in this.requestHash)
			{
				bytesNum += header.bytesLoaded
			}
			return bytesNum
		}
		
		public function get bytesTotal():Number
		{
			var bytesNum:Number = 0
			for each(var header:RequestHeader in this.requestHash)
			{
				bytesNum += header.bytesTotal
			}
			return bytesNum
		}
		
		public static var DEFAULT_MANAGER:LoaderManager= new LoaderManager()
		
	}
}